﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using N2;
using N2.Configuration;
using N2.Definitions;
using N2.Definitions.Static;
using N2.Plugin;
using N2.Web.UI;

namespace AWS.N2CMS.Tags
{
    public class TagDefinitionAppender : IAutoStart
    {
        private readonly IDefinitionManager _definitions;

        private bool _shouldMakeTaggable;

        public TagDefinitionAppender(IDefinitionManager definitions)
        {
            _definitions = definitions;
        }

        #region IAutoStart Members

        public void Start()
        {
            var config =
                Context.Current.Resolve<ConfigurationManagerWrapper>().GetContentSection<TagsConfiguration>("tags") ??
                new TagsConfiguration();
            var configAddedElements = config.Definitions.AddedElements;
            var configRemovedElements = config.Definitions.RemovedElements;
            var allDefinitions = _definitions.GetDefinitions().ToList();
            var toRegister = new List<ItemDefinition>();

            #region Add decorated definitions

            foreach (var definition in allDefinitions)
            {

                if (IsTaggableByDecoration(definition.ItemType))
                {
                    toRegister.Add(definition);
                    continue;
                }
                if (config.AllPagesTaggable && DescriptionDictionary.GetDescription(definition.ItemType).IsPage)
                {
                    toRegister.Add(definition);
                }
            }

            #endregion

            #region Add configured definitions
            
            foreach (TagDefinitionElement element in configAddedElements)
            {
                ItemDefinition definition = allDefinitions.Find(d => d.Discriminator == element.Name);
				
                if(definition == null)
                    throw new ConfigurationErrorsException("The configuration element /n2/tags/definitions/add references a definition '" + element.Name + "' that couldn't be found in the application. Either remove the <add/> configuration or ensure that the corresponding content class exists.");
                
                if (!toRegister.Any(x => x.Discriminator == definition.Discriminator))
                    toRegister.Add(definition);
            }

            #endregion

            #region Remove configured definitions

            foreach (TagDefinitionElement element in configRemovedElements)
            {
                ItemDefinition definition = allDefinitions.Find(d => d.Discriminator == element.Name);
                
                if (definition == null)
                    throw new ConfigurationErrorsException("The configuration element /n2/tags/definitions/remove references a definition '" + element.Name + "' that couldn't be found in the application. Either remove the <remove/> configuration or ensure that the corresponding content class exists.");

                toRegister.Remove(definition);
            }

            #endregion

            foreach (var definition in toRegister)
            {
                var isFancy = true;

                #region Determine if the definition should be fancy
                
                var tagDefinitionElement = configAddedElements.SingleOrDefault(x => x.Name == definition.Discriminator);
                if (tagDefinitionElement != null)
                    isFancy = tagDefinitionElement.IsFancy;
                var attribute = definition.ItemType
                    .GetCustomAttributes(typeof(TaggableAttribute), true)
                    .Cast<TaggableAttribute>().SingleOrDefault();
                if (attribute != null)
                    isFancy = attribute.Fancy;

                #endregion

                TagDefinition(definition, isFancy);
            }
        }

        public void Stop()
        {
        }

        #endregion

        public bool IsTaggableByDecoration(Type type)
        {
            if (type.GetCustomAttributes(typeof(NotTaggableAttribute), true)
                .Cast<NotTaggableAttribute>().Any())
                return false;

            if (type.GetCustomAttributes(typeof (TaggableAttribute), true)
                .Cast<TaggableAttribute>().Any())
                return true;

            return false;
        }

        public void TagDefinition(ItemDefinition definition, bool fancy)
        {
            var tab = new TabContainerAttribute("tagsContainer", "Tags", int.MaxValue);
            var editableTags = new EditableTagsAttribute("Tags", 0, fancy) {Name = "tags", ContainerName = tab.Name};
            tab.AddContained(editableTags);

            definition.Editables.Add(editableTags);
            definition.Add(tab);
        }
    }
}